Completed
Pull Request — master (#97)
by Ruben de
01:16
created

APIClient.updateWallet   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
nc 1
dl 0
loc 5
rs 9.4285
nop 3
1
/* globals onLoadWorkerLoadAsmCrypto */
2
3
var _ = require('lodash'),
4
    q = require('q'),
5
    bitcoin = require('bitcoinjs-lib'),
6
    bitcoinMessage = require('bitcoinjs-message'),
7
8
    bip39 = require("bip39"),
9
    Wallet = require('./wallet'),
10
    RestClient = require('./rest_client'),
11
    Encryption = require('./encryption'),
12
    KeyDerivation = require('./keyderivation'),
13
    EncryptionMnemonic = require('./encryption_mnemonic'),
14
    blocktrail = require('./blocktrail'),
15
    randomBytes = require('randombytes'),
16
    CryptoJS = require('crypto-js'),
17
    webworkifier = require('./webworkifier');
18
19
var useWebWorker = require('./use-webworker')();
20
21
/**
22
 * Bindings to conssume the BlockTrail API
23
 *
24
 * @param options       object{
25
 *                          apiKey: 'API_KEY',
26
 *                          apiSecret: 'API_SECRET',
27
 *                          host: 'defaults to api.blocktrail.com',
28
 *                          network: 'BTC|LTC',
29
 *                          testnet: true|false
30
 *                      }
31
 * @constructor
32
 */
33
var APIClient = function(options) {
34
    var self = this;
35
36
    // handle constructor call without 'new'
37
    if (!(this instanceof APIClient)) {
38
        return new APIClient(options);
39
    }
40
41
    var normalizedNetwork = APIClient.normalizeNetworkFromOptions(options);
42
    console.log(normalizedNetwork);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
43
    options.network = normalizedNetwork[0];
44
    options.testnet = normalizedNetwork[1];
45
    options.regtest = normalizedNetwork[2];
46
    // apiNetwork we allow to be customized for debugging purposes
47
    options.apiNetwork = options.apiNetwork || normalizedNetwork[3];
48
49
    self.bitcoinCash = options.network === "BCC";
50
    self.regtest = options.regtest;
51
    self.testnet = options.testnet;
52
    if (self.bitcoinCash) {
53
        self.network = self.testnet ? bitcoin.networks.bitcoincashtestnet : bitcoin.networks.bitcoincash;
54
    } else {
55
        self.network = self.testnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
56
    }
57
58
    self.feeSanityCheck = typeof options.feeSanityCheck !== "undefined" ? options.feeSanityCheck : true;
59
    self.feeSanityCheckBaseFeeMultiplier = options.feeSanityCheckBaseFeeMultiplier || 200;
60
61
    /**
62
     * @type RestClient
63
     */
64
    self.client = APIClient.initRestClient(options);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
65
};
66
67
APIClient.normalizeNetworkFromOptions = function(options) {
68
    var network = 'BTC';
69
    var testnet = false;
70
    var regtest = false;
71
    var apiNetwork = "BTC";
0 ignored issues
show
Unused Code introduced by
The assignment to variable apiNetwork seems to be never used. Consider removing it.
Loading history...
72
73
    var done = false;
74
75
    if (options.network) {
76
        var lower = options.network.toLowerCase();
77
78
        var m = lower.match(/([rt])?(btc|bch|bcc)/);
79
        if (!m) {
80
            throw new Error("Invalid network [" + options.network + "]");
81
        }
82
83
        if (m[2] === 'btc') {
84
            network = "BTC";
85
        } else {
86
            network = "BCC";
87
        }
88
89
        var prefix = m[1];
90
        if (prefix) {
91
            done = true;
92
            if (prefix === 'r') {
93
                testnet = true;
94
                regtest = true;
95
            } else if (prefix === 't') {
96
                testnet = true;
97
            }
98
        }
99
    }
100
101
    if (!done) {
102
        if (options.regtest) {
103
            testnet = true;
104
            regtest = true;
105
            prefix = "r";
106
        } else if (options.testnet) {
107
            testnet = true;
108
            prefix = "t";
109
        }
110
    }
111
112
    apiNetwork = (prefix || "") + network;
113
114
    return [network, testnet, regtest, apiNetwork];
115
};
116
117
APIClient.initRestClient = function(options) {
118
    // BLOCKTRAIL_SDK_API_ENDPOINT overwrite for development
119
    if (process.env.BLOCKTRAIL_SDK_API_ENDPOINT) {
120
        options.host = process.env.BLOCKTRAIL_SDK_API_ENDPOINT;
121
    }
122
123
    // trim off leading https?://
124
    if (options.host && options.host.indexOf("https://") === 0) {
125
        options.https = true;
126
        options.host = options.host.substr(8);
127
    } else if (options.host && options.host.indexOf("http://") === 0) {
128
        options.https = false;
129
        options.host = options.host.substr(7);
130
    }
131
132
    if (typeof options.https === "undefined") {
133
        options.https = true;
134
    }
135
136
    if (!options.host) {
137
        options.host = 'api.blocktrail.com';
138
    }
139
140
    if (!options.port) {
141
        options.port = options.https ? 443 : 80;
142
    }
143
144
    if (!options.endpoint) {
145
        options.endpoint = "/" + (options.apiVersion || "v1") + (options.apiNetwork ? ("/" + options.apiNetwork) : "");
146
    }
147
148
    return new RestClient(options);
149
};
150
151
var determineDataStorageV2_3 = function(options) {
152
    return q.when(options)
153
        .then(function(options) {
154
            // legacy
155
            if (options.storePrimaryMnemonic) {
156
                options.storeDataOnServer = options.storePrimaryMnemonic;
157
            }
158
159
            // storeDataOnServer=false when primarySeed is provided
160
            if (typeof options.storeDataOnServer === "undefined") {
161
                options.storeDataOnServer = !options.primarySeed;
162
            }
163
164
            return options;
165
        });
166
};
167
168
var produceEncryptedDataV2 = function(options, notify) {
169
    return q.when(options)
170
        .then(function(options) {
171
            if (options.storeDataOnServer) {
172
                if (!options.secret) {
173
                    if (!options.passphrase) {
174
                        throw new blocktrail.WalletCreateError("Can't encrypt data without a passphrase");
175
                    }
176
177
                    notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET);
178
179
                    options.secret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8).toString('hex'); // string because we use it as passphrase
180
                    options.encryptedSecret = CryptoJS.AES.encrypt(options.secret, options.passphrase).toString(CryptoJS.format.OpenSSL); // 'base64' string
181
                }
182
183
                notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY);
184
185
                options.encryptedPrimarySeed = CryptoJS.AES.encrypt(options.primarySeed.toString('base64'), options.secret)
186
                    .toString(CryptoJS.format.OpenSSL); // 'base64' string
187
                options.recoverySecret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8).toString('hex'); // string because we use it as passphrase
188
189
                notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY);
190
191
                options.recoveryEncryptedSecret = CryptoJS.AES.encrypt(options.secret, options.recoverySecret)
192
                                                              .toString(CryptoJS.format.OpenSSL); // 'base64' string
193
            }
194
195
            return options;
196
        });
197
};
198
199
APIClient.prototype.promisedEncrypt = function(pt, pw, iter) {
200
    if (useWebWorker && typeof onLoadWorkerLoadAsmCrypto === "function") {
201
        // generate randomness outside of webworker because many browsers don't have crypto.getRandomValues inside webworkers
202
        var saltBuf = Encryption.generateSalt();
203
        var iv = Encryption.generateIV();
204
205
        return webworkifier.workify(APIClient.prototype.promisedEncrypt, function factory() {
206
            return require('./webworker');
207
        }, onLoadWorkerLoadAsmCrypto, {
208
            method: 'Encryption.encryptWithSaltAndIV',
209
            pt: pt,
210
            pw: pw,
211
            saltBuf: saltBuf,
212
            iv: iv,
213
            iterations: iter
214
        })
215
            .then(function(data) {
216
                return Buffer.from(data.cipherText.buffer);
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
217
            });
218
    } else {
219
        try {
220
            return q.when(Encryption.encrypt(pt, pw, iter));
221
        } catch (e) {
222
            return q.reject(e);
223
        }
224
    }
225
};
226
227
APIClient.prototype.promisedDecrypt = function(ct, pw) {
228
    if (useWebWorker && typeof onLoadWorkerLoadAsmCrypto === "function") {
229
        return webworkifier.workify(APIClient.prototype.promisedDecrypt, function() {
230
            return require('./webworker');
231
        }, onLoadWorkerLoadAsmCrypto, {
232
            method: 'Encryption.decrypt',
233
            ct: ct,
234
            pw: pw
235
        })
236
            .then(function(data) {
237
                return Buffer.from(data.plainText.buffer);
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
238
            });
239
    } else {
240
        try {
241
            return q.when(Encryption.decrypt(ct, pw));
242
        } catch (e) {
243
            return q.reject(e);
244
        }
245
    }
246
};
247
248
APIClient.prototype.produceEncryptedDataV3 = function(options, notify) {
249
    var self = this;
250
251
    return q.when(options)
252
        .then(function(options) {
253
            if (options.storeDataOnServer) {
254
                return q.when()
255
                    .then(function() {
256
                        if (!options.secret) {
257
                            if (!options.passphrase) {
258
                                throw new blocktrail.WalletCreateError("Can't encrypt data without a passphrase");
259
                            }
260
261
                            notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET);
262
263
                            // -> now a buffer
264
                            options.secret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
265
266
                            // -> now a buffer
267
                            return self.promisedEncrypt(options.secret, new Buffer(options.passphrase), KeyDerivation.defaultIterations)
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
268
                                .then(function(encryptedSecret) {
269
                                    options.encryptedSecret = encryptedSecret;
270
                                });
271
                        } else {
272
                            if (!(options.secret instanceof Buffer)) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if !(options.secret instanceof Buffer) is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
273
                                throw new Error('Secret must be a buffer');
274
                            }
275
                        }
276
                    })
277
                    .then(function() {
278
                        notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY);
279
280
                        return self.promisedEncrypt(options.primarySeed, options.secret, KeyDerivation.subkeyIterations)
281
                            .then(function(encryptedPrimarySeed) {
282
                                options.encryptedPrimarySeed = encryptedPrimarySeed;
283
                            });
284
                    })
285
                    .then(function() {
286
                        // skip generating recovery secret when explicitly set to false
287
                        if (options.recoverySecret === false) {
288
                            return;
289
                        }
290
291
                        notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY);
292
                        if (!options.recoverySecret) {
293
                            options.recoverySecret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
294
                        }
295
296
                        return self.promisedEncrypt(options.secret, options.recoverySecret, KeyDerivation.defaultIterations)
297
                            .then(function(recoveryEncryptedSecret) {
298
                                options.recoveryEncryptedSecret = recoveryEncryptedSecret;
299
                            });
300
                    })
301
                    .then(function() {
302
                        return options;
303
                    });
304
            } else {
305
                return options;
306
            }
307
        });
308
};
309
310
var doRemainingWalletDataV2_3 = function(options, network, notify) {
311
    return q.when(options)
312
        .then(function(options) {
313
            if (!options.backupPublicKey) {
314
                options.backupSeed = options.backupSeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
315
            }
316
317
            notify(APIClient.CREATE_WALLET_PROGRESS_PRIMARY);
318
319
            options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.primarySeed, network);
320
321
            notify(APIClient.CREATE_WALLET_PROGRESS_BACKUP);
322
323
            if (!options.backupPublicKey) {
324
                options.backupPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.backupSeed, network);
325
                options.backupPublicKey = options.backupPrivateKey.neutered();
326
            }
327
328
            options.primaryPublicKey = options.primaryPrivateKey.deriveHardened(options.keyIndex).neutered();
329
330
            notify(APIClient.CREATE_WALLET_PROGRESS_SUBMIT);
331
332
            return options;
333
        });
334
};
335
336
APIClient.prototype.mnemonicToPrivateKey = function(mnemonic, passphrase, cb) {
337
    var self = this;
338
339
    var deferred = q.defer();
340
    deferred.promise.spreadNodeify(cb);
341
342
    deferred.resolve(q.fcall(function() {
343
        return self.mnemonicToSeedHex(mnemonic, passphrase).then(function(seedHex) {
344
            return bitcoin.HDNode.fromSeedHex(seedHex, self.network);
345
        });
346
    }));
347
348
    return deferred.promise;
349
};
350
351
APIClient.prototype.mnemonicToSeedHex = function(mnemonic, passphrase) {
352
    var self = this;
353
354
    if (useWebWorker) {
355
        return webworkifier.workify(self.mnemonicToSeedHex, function() {
356
            return require('./webworker');
357
        }, {method: 'mnemonicToSeedHex', mnemonic: mnemonic, passphrase: passphrase})
358
            .then(function(data) {
359
                return data.seed;
360
            });
361
    } else {
362
        try {
363
            return q.when(bip39.mnemonicToSeedHex(mnemonic, passphrase));
364
        } catch (e) {
365
            return q.reject(e);
366
        }
367
    }
368
};
369
370
APIClient.prototype.resolvePrimaryPrivateKeyFromOptions = function(options, cb) {
371
    var self = this;
372
373
    var deferred = q.defer();
374
    deferred.promise.nodeify(cb);
375
376
    try {
377
        // avoid conflicting options
378
        if (options.passphrase && options.password) {
379
            throw new blocktrail.WalletCreateError("Can't specify passphrase and password");
380
        }
381
        // normalize passphrase/password
382
        options.passphrase = options.passphrase || options.password;
383
        delete options.password;
384
385
        // avoid conflicting options
386
        if (options.primaryMnemonic && options.primarySeed) {
387
            throw new blocktrail.WalletInitError("Can only specify one of; Primary Mnemonic or Primary Seed");
388
        }
389
390
        // avoid deprecated options
391
        if (options.primaryPrivateKey) {
392
            throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
393
        }
394
395
        // make sure we have at least one thing to use
396
        if (!options.primaryMnemonic && !options.primarySeed) {
397
            throw new blocktrail.WalletInitError("Need to specify at least one of; Primary Mnemonic or Primary Seed");
398
        }
399
400
        if (options.primarySeed) {
401
            self.primarySeed = options.primarySeed;
402
            options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(self.primarySeed, self.network);
403
            deferred.resolve(options);
404
        } else {
405
            if (!options.passphrase) {
406
                throw new blocktrail.WalletInitError("Can't init wallet with Primary Mnemonic without a passphrase");
407
            }
408
409
            self.mnemonicToSeedHex(options.primaryMnemonic, options.passphrase)
410
                .then(function(seedHex) {
411
                    try {
412
                        options.primarySeed = new Buffer(seedHex, 'hex');
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
413
                        options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.primarySeed, self.network);
414
                        deferred.resolve(options);
415
                    } catch (e) {
416
                        deferred.reject(e);
417
                    }
418
                }, function(e) {
419
                    deferred.reject(e);
420
                });
421
        }
422
    } catch (e) {
423
        deferred.reject(e);
424
    }
425
426
    return deferred.promise;
427
};
428
429
APIClient.prototype.resolveBackupPublicKeyFromOptions = function(options, cb) {
430
    var self = this;
431
432
    var deferred = q.defer();
433
    deferred.promise.nodeify(cb);
434
435
    try {
436
        // avoid conflicting options
437
        if (options.backupMnemonic && options.backupPublicKey) {
438
            throw new blocktrail.WalletInitError("Can only specify one of; Backup Mnemonic or Backup PublicKey");
439
        }
440
441
        // make sure we have at least one thing to use
442
        if (!options.backupMnemonic && !options.backupPublicKey) {
443
            throw new blocktrail.WalletInitError("Need to specify at least one of; Backup Mnemonic or Backup PublicKey");
444
        }
445
446
        if (options.backupPublicKey) {
447
            if (options.backupPublicKey instanceof bitcoin.HDNode) {
448
                deferred.resolve(options);
449
            } else {
450
                options.backupPublicKey = bitcoin.HDNode.fromBase58(options.backupPublicKey, self.network);
451
                deferred.resolve(options);
452
            }
453
        } else {
454
            self.mnemonicToPrivateKey(options.backupMnemonic, "").then(function(backupPrivateKey) {
455
                options.backupPublicKey = backupPrivateKey.neutered();
456
                deferred.resolve(options);
457
            }, function(e) {
458
                deferred.reject(e);
459
            });
460
        }
461
    } catch (e) {
462
        deferred.reject(e);
463
    }
464
465
    return deferred.promise;
466
};
467
468
APIClient.prototype.debugAuth = function(cb) {
469
    var self = this;
470
471
    return self.client.get("/debug/http-signature", null, true, cb);
472
};
473
474
/**
475
 * get a single address
476
 *
477
 * @param address      string       address hash
478
 * @param [cb]          function    callback function to call when request is complete
479
 * @return q.Promise
480
 */
481
APIClient.prototype.address = function(address, cb) {
482
    var self = this;
483
484
    return self.client.get("/address/" + address, null, cb);
485
};
486
487
APIClient.prototype.addresses = function(addresses, cb) {
488
    var self = this;
489
490
    return self.client.post("/address", null, {"addresses": addresses}, cb);
491
};
492
493
/**
494
 * get all transactions for an address (paginated)
495
 *
496
 * @param address       string      address hash
497
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
498
 * @param [cb]          function    callback function to call when request is complete
499
 * @return q.Promise
500
 */
501
APIClient.prototype.addressTransactions = function(address, params, cb) {
502
    var self = this;
503
504
    if (typeof params === "function") {
505
        cb = params;
506
        params = null;
507
    }
508
509
    return self.client.get("/address/" + address + "/transactions", params, cb);
510
};
511
512
/**
513
 * get all transactions for a batch of addresses (paginated)
514
 *
515
 * @param addresses     array       address hashes
516
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
517
 * @param [cb]          function    callback function to call when request is complete
518
 * @return q.Promise
519
 */
520
APIClient.prototype.batchAddressHasTransactions = function(addresses, params, cb) {
521
    var self = this;
522
523
    if (typeof params === "function") {
524
        cb = params;
525
        params = null;
526
    }
527
528
    return self.client.post("/address/has-transactions", params, {"addresses": addresses}, cb);
529
};
530
531
/**
532
 * get all unconfirmed transactions for an address (paginated)
533
 *
534
 * @param address       string      address hash
535
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
536
 * @param [cb]          function    callback function to call when request is complete
537
 * @return q.Promise
538
 */
539
APIClient.prototype.addressUnconfirmedTransactions = function(address, params, cb) {
540
    var self = this;
541
542
    if (typeof params === "function") {
543
        cb = params;
544
        params = null;
545
    }
546
547
    return self.client.get("/address/" + address + "/unconfirmed-transactions", params, cb);
548
};
549
550
/**
551
 * get all unspent outputs for an address (paginated)
552
 *
553
 * @param address       string      address hash
554
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
555
 * @param [cb]          function    callback function to call when request is complete
556
 * @return q.Promise
557
 */
558
APIClient.prototype.addressUnspentOutputs = function(address, params, cb) {
559
    var self = this;
560
561
    if (typeof params === "function") {
562
        cb = params;
563
        params = null;
564
    }
565
566
    return self.client.get("/address/" + address + "/unspent-outputs", params, cb);
567
};
568
569
/**
570
 * get all unspent outputs for a batch of addresses (paginated)
571
 *
572
 * @param addresses     array       address hashes
573
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
574
 * @param [cb]          function    callback function to call when request is complete
575
 * @return q.Promise
576
 */
577
APIClient.prototype.batchAddressUnspentOutputs = function(addresses, params, cb) {
578
    var self = this;
579
580
    if (typeof params === "function") {
581
        cb = params;
582
        params = null;
583
    }
584
585
    return self.client.post("/address/unspent-outputs", params, {"addresses": addresses}, cb);
586
};
587
588
/**
589
 * verify ownership of an address
590
 *
591
 * @param address       string      address hash
592
 * @param signature     string      a signed message (the address hash) using the private key of the address
593
 * @param [cb]          function    callback function to call when request is complete
594
 * @return q.Promise
595
 */
596
APIClient.prototype.verifyAddress = function(address, signature, cb) {
597
    var self = this;
598
599
    return self.client.post("/address/" + address + "/verify", null, {signature: signature}, cb);
600
};
601
602
/**
603
 * get all blocks (paginated)
604
 *
605
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
606
 * @param [cb]          function    callback function to call when request is complete
607
 * @return q.Promise
608
 */
609
APIClient.prototype.allBlocks = function(params, cb) {
610
    var self = this;
611
612
    if (typeof params === "function") {
613
        cb = params;
614
        params = null;
615
    }
616
617
    return self.client.get("/all-blocks", params, cb);
618
};
619
620
/**
621
 * get a block
622
 *
623
 * @param block         string|int  a block hash or a block height
624
 * @param [cb]          function    callback function to call when request is complete
625
 * @return q.Promise
626
 */
627
APIClient.prototype.block = function(block, cb) {
628
    var self = this;
629
630
    return self.client.get("/block/" + block, null, cb);
631
};
632
633
/**
634
 * get the latest block
635
 *
636
 * @param [cb]          function    callback function to call when request is complete
637
 * @return q.Promise
638
 */
639
APIClient.prototype.blockLatest = function(cb) {
640
    var self = this;
641
642
    return self.client.get("/block/latest", null, cb);
643
};
644
645
/**
646
 * get all transactions for a block (paginated)
647
 *
648
 * @param block         string|int  a block hash or a block height
649
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
650
 * @param [cb]          function    callback function to call when request is complete
651
 * @return q.Promise
652
 */
653
APIClient.prototype.blockTransactions = function(block, params, cb) {
654
    var self = this;
655
656
    if (typeof params === "function") {
657
        cb = params;
658
        params = null;
659
    }
660
661
    return self.client.get("/block/" + block + "/transactions", params, cb);
662
};
663
664
/**
665
 * get a single transaction
666
 *
667
 * @param tx            string      transaction hash
668
 * @param [cb]          function    callback function to call when request is complete
669
 * @return q.Promise
670
 */
671
APIClient.prototype.transaction = function(tx, cb) {
672
    var self = this;
673
674
    return self.client.get("/transaction/" + tx, null, cb);
675
};
676
677
/**
678
 * get a batch of transactions
679
 *
680
 * @param txs           string[]    list of transaction hashes (txId)
681
 * @param [cb]          function    callback function to call when request is complete
682
 * @return q.Promise
683
 */
684
APIClient.prototype.transactions = function(txs, cb) {
685
    var self = this;
686
687
    return self.client.post("/transactions", null, txs, cb, false);
688
};
689
690
/**
691
 * get a paginated list of all webhooks associated with the api user
692
 *
693
 * @param [params]      object      pagination: {page: 1, limit: 20}
694
 * @param [cb]          function    callback function to call when request is complete
695
 * @return q.Promise
696
 */
697
APIClient.prototype.allWebhooks = function(params, cb) {
698
    var self = this;
699
700
    if (typeof params === "function") {
701
        cb = params;
702
        params = null;
703
    }
704
705
    return self.client.get("/webhooks", params, cb);
706
};
707
708
/**
709
 * create a new webhook
710
 *
711
 * @param url           string      the url to receive the webhook events
712
 * @param [identifier]  string      a unique identifier associated with the webhook
713
 * @param [cb]          function    callback function to call when request is complete
714
 * @return q.Promise
715
 */
716
APIClient.prototype.setupWebhook = function(url, identifier, cb) {
717
    var self = this;
718
719
    if (typeof identifier === "function") {
720
        //mimic function overloading
721
        cb = identifier;
722
        identifier = null;
723
    }
724
725
    return self.client.post("/webhook", null, {url: url, identifier: identifier}, cb);
726
};
727
728
/**
729
 * Converts a cash address to the legacy (base58) format
730
 * @param {string} input
731
 * @returns {string}
732
 */
733
APIClient.prototype.getLegacyBitcoinCashAddress = function(input) {
734
    if (this.network === bitcoin.networks.bitcoincash || this.network === bitcoin.networks.bitcoincashtestnet) {
735
        var address;
736
        try {
737
            bitcoin.address.fromBase58Check(input, this.network);
738
            return input;
739
        } catch (e) {}
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
740
741
        address = bitcoin.address.fromCashAddress(input, this.network);
742
        var prefix;
743
        if (address.version === bitcoin.script.types.P2PKH) {
744
            prefix = this.network.pubKeyHash;
745
        } else if (address.version === bitcoin.script.types.P2SH) {
746
            prefix = this.network.scriptHash;
747
        } else {
748
            throw new Error("Unsupported address type");
749
        }
750
751
        return bitcoin.address.toBase58Check(address.hash, prefix);
752
    }
753
754
    throw new Error("Cash addresses only work on bitcoin cash");
755
};
756
757
/**
758
 * Converts a legacy bitcoin to the new cashaddr format
759
 * @param {string} input
760
 * @returns {string}
761
 */
762
APIClient.prototype.getCashAddressFromLegacyAddress = function(input) {
763
    if (this.network === bitcoin.networks.bitcoincash || this.network === bitcoin.networks.bitcoincashtestnet) {
764
        var address;
765
        try {
766
            bitcoin.address.fromCashAddress(input, this.network);
767
            return input;
768
        } catch (e) {}
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
769
770
        address = bitcoin.address.fromBase58Check(input, this.network);
771
        var scriptType;
772
        if (address.version === this.network.pubKeyHash) {
773
            scriptType = bitcoin.script.types.P2PKH;
774
        } else if (address.version === this.network.scriptHash) {
775
            scriptType = bitcoin.script.types.P2SH;
776
        } else {
777
            throw new Error("Unsupported address type");
778
        }
779
780
        return bitcoin.address.toCashAddress(address.hash, scriptType, this.network.cashAddrPrefix);
781
    }
782
783
    throw new Error("Cash addresses only work on bitcoin cash");
784
};
785
786
/**
787
 * get an existing webhook by it's identifier
788
 *
789
 * @param identifier    string      the unique identifier of the webhook to get
790
 * @param [cb]          function    callback function to call when request is complete
791
 * @return q.Promise
792
 */
793
APIClient.prototype.getWebhook = function(identifier, cb) {
794
    var self = this;
795
796
    return self.client.get("/webhook/" + identifier, null, cb);
797
};
798
799
/**
800
 * update an existing webhook
801
 *
802
 * @param identifier    string      the unique identifier of the webhook
803
 * @param webhookData   object      the data to update: {identifier: newIdentifier, url:newUrl}
804
 * @param [cb]          function    callback function to call when request is complete
805
 * @return q.Promise
806
 */
807
APIClient.prototype.updateWebhook = function(identifier, webhookData, cb) {
808
    var self = this;
809
810
    return self.client.put("/webhook/" + identifier, null, webhookData, cb);
811
};
812
813
/**
814
 * deletes an existing webhook and any event subscriptions associated with it
815
 *
816
 * @param identifier    string      the unique identifier of the webhook
817
 * @param [cb]          function    callback function to call when request is complete
818
 * @return q.Promise
819
 */
820
APIClient.prototype.deleteWebhook = function(identifier, cb) {
821
    var self = this;
822
823
    return self.client.delete("/webhook/" + identifier, null, null, cb);
824
};
825
826
/**
827
 * get a paginated list of all the events a webhook is subscribed to
828
 *
829
 * @param identifier    string      the unique identifier of the webhook
830
 * @param [params]      object      pagination: {page: 1, limit: 20}
831
 * @param [cb]          function    callback function to call when request is complete
832
 * @return q.Promise
833
 */
834
APIClient.prototype.getWebhookEvents = function(identifier, params, cb) {
835
    var self = this;
836
837
    if (typeof params === "function") {
838
        cb = params;
839
        params = null;
840
    }
841
842
    return self.client.get("/webhook/" + identifier + "/events", params, cb);
843
};
844
845
/**
846
 * subscribes a webhook to transaction events for a particular transaction
847
 *
848
 * @param identifier    string      the unique identifier of the webhook
849
 * @param transaction   string      the transaction hash
850
 * @param confirmations integer     the amount of confirmations to send
851
 * @param [cb]          function    callback function to call when request is complete
852
 * @return q.Promise
853
 */
854
APIClient.prototype.subscribeTransaction = function(identifier, transaction, confirmations, cb) {
855
    var self = this;
856
    var postData = {
857
        'event_type': 'transaction',
858
        'transaction': transaction,
859
        'confirmations': confirmations
860
    };
861
862
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
863
};
864
865
/**
866
 * subscribes a webhook to transaction events on a particular address
867
 *
868
 * @param identifier    string      the unique identifier of the webhook
869
 * @param address       string      the address hash
870
 * @param confirmations integer     the amount of confirmations to send
871
 * @param [cb]          function    callback function to call when request is complete
872
 * @return q.Promise
873
 */
874
APIClient.prototype.subscribeAddressTransactions = function(identifier, address, confirmations, cb) {
875
    var self = this;
876
    var postData = {
877
        'event_type': 'address-transactions',
878
        'address': address,
879
        'confirmations': confirmations
880
    };
881
882
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
883
};
884
885
/**
886
 * batch subscribes a webhook to multiple transaction events
887
 *
888
 * @param  identifier   string      the unique identifier of the webhook
889
 * @param  batchData    array       An array of objects containing batch event data:
890
 *                                  {address : 'address', confirmations : 'confirmations']
891
 *                                  where address is the address to subscribe to and confirmations (optional) is the amount of confirmations to send
892
 * @param [cb]          function    callback function to call when request is complete
893
 * @return q.Promise
894
 */
895
APIClient.prototype.batchSubscribeAddressTransactions = function(identifier, batchData, cb) {
896
    var self = this;
897
    batchData.forEach(function(record) {
898
        record.event_type = 'address-transactions';
899
    });
900
901
    return self.client.post("/webhook/" + identifier + "/events/batch", null, batchData, cb);
902
};
903
904
/**
905
 * subscribes a webhook to a new block event
906
 *
907
 * @param identifier    string      the unique identifier of the webhook
908
 * @param [cb]          function    callback function to call when request is complete
909
 * @return q.Promise
910
 */
911
APIClient.prototype.subscribeNewBlocks = function(identifier, cb) {
912
    var self = this;
913
    var postData = {
914
        'event_type': 'block'
915
    };
916
917
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
918
};
919
920
/**
921
 * removes an address transaction event subscription from a webhook
922
 *
923
 * @param identifier    string      the unique identifier of the webhook
924
 * @param address       string      the address hash
925
 * @param [cb]          function    callback function to call when request is complete
926
 * @return q.Promise
927
 */
928
APIClient.prototype.unsubscribeAddressTransactions = function(identifier, address, cb) {
929
    var self = this;
930
931
    return self.client.delete("/webhook/" + identifier + "/address-transactions/" + address, null, null, cb);
932
};
933
934
/**
935
 * removes an transaction event subscription from a webhook
936
 *
937
 * @param identifier    string      the unique identifier of the webhook
938
 * @param transaction   string      the transaction hash
939
 * @param [cb]          function    callback function to call when request is complete
940
 * @return q.Promise
941
 */
942
APIClient.prototype.unsubscribeTransaction = function(identifier, transaction, cb) {
943
    var self = this;
944
945
    return self.client.delete("/webhook/" + identifier + "/transaction/" + transaction, null, null, cb);
946
};
947
948
/**
949
 * removes a block event subscription from a webhook
950
 *
951
 * @param identifier    string      the unique identifier of the webhook
952
 * @param [cb]          function    callback function to call when request is complete
953
 * @return q.Promise
954
 */
955
APIClient.prototype.unsubscribeNewBlocks = function(identifier, cb) {
956
    var self = this;
957
958
    return self.client.delete("/webhook/" + identifier + "/block", null, null, cb);
959
};
960
961
/**
962
 * initialize an existing wallet
963
 *
964
 * Either takes two argument:
965
 * @param options       object      {}
966
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
967
 *
968
 * Or takes three arguments (old, deprecated syntax):
969
 * @param identifier    string      the wallet identifier to be initialized
0 ignored issues
show
Documentation introduced by
The parameter identifier does not exist. Did you maybe forget to remove this comment?
Loading history...
970
 * @param passphrase    string      the password to decrypt the mnemonic with
0 ignored issues
show
Documentation introduced by
The parameter passphrase does not exist. Did you maybe forget to remove this comment?
Loading history...
971
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
0 ignored issues
show
Documentation introduced by
The parameter cb has already been documented on line 966. The second definition is ignored.
Loading history...
972
 *
973
 * @returns {q.Promise}
974
 */
975
APIClient.prototype.initWallet = function(options, cb) {
976
    var self = this;
977
978
    if (typeof options !== "object") {
979
        // get the old-style arguments
980
        options = {
981
            identifier: arguments[0],
982
            passphrase: arguments[1]
983
        };
984
985
        cb = arguments[2];
986
    }
987
988
    if (options.check_backup_key) {
989
        if (typeof options.check_backup_key !== "string") {
990
            throw new Error("Invalid input, must provide the backup key as a string (the xpub)");
991
        }
992
    }
993
994
    var deferred = q.defer();
995
    deferred.promise.spreadNodeify(cb);
996
997
    var identifier = options.identifier;
998
999
    if (!identifier) {
1000
        deferred.reject(new blocktrail.WalletInitError("Identifier is required"));
1001
        return deferred.promise;
1002
    }
1003
1004
    deferred.resolve(self.client.get("/wallet/" + identifier, null, true).then(function(result) {
1005
        var keyIndex = options.keyIndex || result.key_index;
1006
1007
        options.walletVersion = result.wallet_version;
1008
1009
        if (options.check_backup_key) {
1010
            if (options.check_backup_key !== result.backup_public_key[0]) {
1011
                throw new Error("Backup key returned from server didn't match our own copy");
1012
            }
1013
        }
1014
        var backupPublicKey = bitcoin.HDNode.fromBase58(result.backup_public_key[0], self.network);
1015
        var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1016
            return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1017
        });
1018
        var primaryPublicKeys = _.mapValues(result.primary_public_keys, function(primaryPublicKey) {
1019
            return bitcoin.HDNode.fromBase58(primaryPublicKey[0], self.network);
1020
        });
1021
1022
        // initialize wallet
1023
        var wallet = new Wallet(
1024
            self,
1025
            identifier,
1026
            options.walletVersion,
1027
            result.primary_mnemonic,
1028
            result.encrypted_primary_seed,
1029
            result.encrypted_secret,
1030
            primaryPublicKeys,
1031
            backupPublicKey,
1032
            blocktrailPublicKeys,
1033
            keyIndex,
1034
            result.segwit || 0,
1035
            self.testnet,
1036
            result.checksum,
1037
            result.upgrade_key_index,
1038
            options.useCashAddress,
1039
            options.bypassNewAddressCheck
1040
        );
1041
1042
        wallet.recoverySecret = result.recovery_secret;
1043
1044
        if (!options.readOnly) {
1045
            return wallet.unlock(options).then(function() {
1046
                return wallet;
1047
            });
1048
        } else {
1049
            return wallet;
1050
        }
1051
    }));
1052
1053
    return deferred.promise;
1054
};
1055
1056
APIClient.CREATE_WALLET_PROGRESS_START = 0;
1057
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET = 4;
1058
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY = 5;
1059
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY = 6;
1060
APIClient.CREATE_WALLET_PROGRESS_PRIMARY = 10;
1061
APIClient.CREATE_WALLET_PROGRESS_BACKUP = 20;
1062
APIClient.CREATE_WALLET_PROGRESS_SUBMIT = 30;
1063
APIClient.CREATE_WALLET_PROGRESS_INIT = 40;
1064
APIClient.CREATE_WALLET_PROGRESS_DONE = 100;
1065
1066
/**
1067
 * create a new wallet
1068
 *   - will generate a new primary seed and backup seed
1069
 *
1070
 * Either takes two argument:
1071
 * @param options       object      {}
1072
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys) // nocommit @TODO
1073
 *
1074
 * For v1 wallets (explicitly specify options.walletVersion=v1):
1075
 * @param options       object      {}
0 ignored issues
show
Documentation introduced by
The parameter options has already been documented on line 1071. The second definition is ignored.
Loading history...
1076
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
0 ignored issues
show
Documentation introduced by
The parameter cb has already been documented on line 1072. The second definition is ignored.
Loading history...
1077
 *
1078
 * Or takes four arguments (old, deprecated syntax):
1079
 * @param identifier    string      the wallet identifier to be initialized
0 ignored issues
show
Documentation introduced by
The parameter identifier does not exist. Did you maybe forget to remove this comment?
Loading history...
1080
 * @param passphrase    string      the password to decrypt the mnemonic with
0 ignored issues
show
Documentation introduced by
The parameter passphrase does not exist. Did you maybe forget to remove this comment?
Loading history...
1081
 * @param keyIndex      int         override for the blocktrail cosign key to use (for development purposes)
0 ignored issues
show
Documentation introduced by
The parameter keyIndex does not exist. Did you maybe forget to remove this comment?
Loading history...
1082
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
0 ignored issues
show
Documentation introduced by
The parameter cb has already been documented on line 1072. The second definition is ignored.
Loading history...
1083
 * @returns {q.Promise}
1084
 */
1085
APIClient.prototype.createNewWallet = function(options, cb) {
1086
    /* jshint -W071, -W074 */
1087
1088
    var self = this;
1089
1090
    if (typeof options !== "object") {
1091
        // get the old-style arguments
1092
        var identifier = arguments[0];
1093
        var passphrase = arguments[1];
1094
        var keyIndex = arguments[2];
1095
        cb = arguments[3];
1096
1097
        // keyIndex is optional
1098
        if (typeof keyIndex === "function") {
1099
            cb = keyIndex;
1100
            keyIndex = null;
1101
        }
1102
1103
        options = {
1104
            identifier: identifier,
1105
            passphrase: passphrase,
1106
            keyIndex: keyIndex
1107
        };
1108
    }
1109
1110
    // default to v3
1111
    options.walletVersion = options.walletVersion || Wallet.WALLET_VERSION_V3;
1112
1113
    var deferred = q.defer();
1114
    deferred.promise.spreadNodeify(cb);
1115
1116
    q.nextTick(function() {
1117
        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_START);
1118
1119
        options.keyIndex = options.keyIndex || 0;
1120
        options.passphrase = options.passphrase || options.password;
1121
        delete options.password;
1122
1123
        if (!options.identifier) {
1124
            deferred.reject(new blocktrail.WalletCreateError("Identifier is required"));
1125
            return deferred.promise;
1126
        }
1127
1128
        if (options.walletVersion === Wallet.WALLET_VERSION_V1) {
1129
            self._createNewWalletV1(options)
1130
                .progress(function(p) { deferred.notify(p); })
1131
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1132
            ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1133
        } else if (options.walletVersion === Wallet.WALLET_VERSION_V2) {
1134
            self._createNewWalletV2(options)
1135
                .progress(function(p) { deferred.notify(p); })
1136
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1137
            ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1138
        } else if (options.walletVersion === Wallet.WALLET_VERSION_V3) {
1139
            self._createNewWalletV3(options)
1140
                .progress(function(p) { deferred.notify(p); })
1141
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1142
            ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1143
        } else {
1144
            deferred.reject(new blocktrail.WalletCreateError("Invalid wallet version!"));
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1145
        }
1146
    });
1147
1148
    return deferred.promise;
1149
};
1150
1151
APIClient.prototype._createNewWalletV1 = function(options) {
1152
    var self = this;
1153
1154
    var deferred = q.defer();
1155 View Code Duplication
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1156
    q.nextTick(function() {
1157
1158
        if (!options.primaryMnemonic && !options.primarySeed) {
1159
            if (!options.passphrase && !options.password) {
1160
                deferred.reject(new blocktrail.WalletCreateError("Can't generate Primary Mnemonic without a passphrase"));
1161
                return deferred.promise;
1162
            } else {
1163
                options.primaryMnemonic = bip39.generateMnemonic(Wallet.WALLET_ENTROPY_BITS);
1164
                if (options.storePrimaryMnemonic !== false) {
1165
                    options.storePrimaryMnemonic = true;
1166
                }
1167
            }
1168
        }
1169
1170
        if (!options.backupMnemonic && !options.backupPublicKey) {
1171
            options.backupMnemonic = bip39.generateMnemonic(Wallet.WALLET_ENTROPY_BITS);
1172
        }
1173
1174
        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_PRIMARY);
1175
1176
        self.resolvePrimaryPrivateKeyFromOptions(options)
1177
            .then(function(options) {
1178
                deferred.notify(APIClient.CREATE_WALLET_PROGRESS_BACKUP);
1179
1180
                return self.resolveBackupPublicKeyFromOptions(options)
1181
                    .then(function(options) {
1182
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_SUBMIT);
1183
1184
                        // create a checksum of our private key which we'll later use to verify we used the right password
1185
                        var pubKeyHash = bitcoin.crypto.hash160(options.primaryPrivateKey.getPublicKeyBuffer());
1186
                        var checksum = bitcoin.address.toBase58Check(pubKeyHash, self.network.pubKeyHash);
1187
                        var keyIndex = options.keyIndex;
1188
1189
                        var primaryPublicKey = options.primaryPrivateKey.deriveHardened(keyIndex).neutered();
1190
1191
                        // send the public keys to the server to store them
1192
                        //  and the mnemonic, which is safe because it's useless without the password
1193
                        return self.storeNewWalletV1(
1194
                            options.identifier,
1195
                            [primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1196
                            [options.backupPublicKey.toBase58(), "M"],
1197
                            options.storePrimaryMnemonic ? options.primaryMnemonic : false,
1198
                            checksum,
1199
                            keyIndex,
1200
                            options.segwit || null
1201
                        )
1202
                            .then(function(result) {
1203
                                deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1204
1205
                                var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1206
                                    return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1207
                                });
1208
1209
                                var wallet = new Wallet(
1210
                                    self,
1211
                                    options.identifier,
1212
                                    Wallet.WALLET_VERSION_V1,
1213
                                    options.primaryMnemonic,
1214
                                    null,
1215
                                    null,
1216
                                    {keyIndex: primaryPublicKey},
1217
                                    options.backupPublicKey,
1218
                                    blocktrailPublicKeys,
1219
                                    keyIndex,
1220
                                    result.segwit || 0,
1221
                                    self.testnet,
1222
                                    checksum,
1223
                                    result.upgrade_key_index,
1224
                                    options.useCashAddress,
1225
                                    options.bypassNewAddressCheck
1226
                                );
1227
1228
                                return wallet.unlock({
1229
                                    walletVersion: Wallet.WALLET_VERSION_V1,
1230
                                    passphrase: options.passphrase,
1231
                                    primarySeed: options.primarySeed,
1232
                                    primaryMnemonic: null // explicit null
1233
                                }).then(function() {
1234
                                    deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1235
                                    return [
1236
                                        wallet,
1237
                                        {
1238
                                            walletVersion: wallet.walletVersion,
1239
                                            primaryMnemonic: options.primaryMnemonic,
1240
                                            backupMnemonic: options.backupMnemonic,
1241
                                            blocktrailPublicKeys: blocktrailPublicKeys
1242
                                        }
1243
                                    ];
1244
                                });
1245
                            });
1246
                    }
1247
                );
1248
            })
1249
            .then(
1250
            function(r) {
1251
                deferred.resolve(r);
1252
            },
1253
            function(e) {
1254
                deferred.reject(e);
1255
            }
1256
        )
1257
        ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1258
    });
1259
1260
    return deferred.promise;
1261
};
1262
1263
APIClient.prototype._createNewWalletV2 = function(options) {
1264 View Code Duplication
    var self = this;
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1265
1266
    var deferred = q.defer();
1267
1268
    // avoid modifying passed options
1269
    options = _.merge({}, options);
1270
1271
    determineDataStorageV2_3(options)
1272
        .then(function(options) {
1273
            options.passphrase = options.passphrase || options.password;
1274
            delete options.password;
1275
1276
            // avoid deprecated options
1277
            if (options.primaryPrivateKey) {
1278
                throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
1279
            }
1280
1281
            // seed should be provided or generated
1282
            options.primarySeed = options.primarySeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
1283
1284
            return options;
1285
        })
1286
        .then(function(options) {
1287
            return produceEncryptedDataV2(options, deferred.notify.bind(deferred));
1288
        })
1289
        .then(function(options) {
1290
            return doRemainingWalletDataV2_3(options, self.network, deferred.notify.bind(deferred));
1291
        })
1292
        .then(function(options) {
1293
            // create a checksum of our private key which we'll later use to verify we used the right password
1294
            var pubKeyHash = bitcoin.crypto.hash160(options.primaryPrivateKey.getPublicKeyBuffer());
1295
            var checksum = bitcoin.address.toBase58Check(pubKeyHash, self.network.pubKeyHash);
1296
            var keyIndex = options.keyIndex;
1297
1298
            // send the public keys and encrypted data to server
1299
            return self.storeNewWalletV2(
1300
                options.identifier,
1301
                [options.primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1302
                [options.backupPublicKey.toBase58(), "M"],
1303
                options.storeDataOnServer ? options.encryptedPrimarySeed : false,
1304
                options.storeDataOnServer ? options.encryptedSecret : false,
1305
                options.storeDataOnServer ? options.recoverySecret : false,
1306
                checksum,
1307
                keyIndex,
1308
                options.support_secret || null,
1309
                options.segwit || null
1310
            )
1311
                .then(
1312
                function(result) {
1313
                    deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1314
1315
                    var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1316
                        return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1317
                    });
1318
1319
                    var wallet = new Wallet(
1320
                        self,
1321
                        options.identifier,
1322
                        Wallet.WALLET_VERSION_V2,
1323
                        null,
1324
                        options.storeDataOnServer ? options.encryptedPrimarySeed : null,
1325
                        options.storeDataOnServer ? options.encryptedSecret : null,
1326
                        {keyIndex: options.primaryPublicKey},
1327
                        options.backupPublicKey,
1328
                        blocktrailPublicKeys,
1329
                        keyIndex,
1330
                        result.segwit || 0,
1331
                        self.testnet,
1332
                        checksum,
1333
                        result.upgrade_key_index,
1334
                        options.useCashAddress,
1335
                        options.bypassNewAddressCheck
1336
                    );
1337
1338
                    // pass along decrypted data to avoid extra work
1339
                    return wallet.unlock({
1340
                        walletVersion: Wallet.WALLET_VERSION_V2,
1341
                        passphrase: options.passphrase,
1342
                        primarySeed: options.primarySeed,
1343
                        secret: options.secret
1344
                    }).then(function() {
1345
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1346
                        return [
1347
                            wallet,
1348
                            {
1349
                                walletVersion: wallet.walletVersion,
1350
                                encryptedPrimarySeed: options.encryptedPrimarySeed ?
1351
                                    bip39.entropyToMnemonic(blocktrail.convert(options.encryptedPrimarySeed, 'base64', 'hex')) :
1352
                                    null,
1353
                                backupSeed: options.backupSeed ? bip39.entropyToMnemonic(options.backupSeed.toString('hex')) : null,
1354
                                recoveryEncryptedSecret: options.recoveryEncryptedSecret ?
1355
                                    bip39.entropyToMnemonic(blocktrail.convert(options.recoveryEncryptedSecret, 'base64', 'hex')) :
1356
                                    null,
1357
                                encryptedSecret: options.encryptedSecret ?
1358
                                    bip39.entropyToMnemonic(blocktrail.convert(options.encryptedSecret, 'base64', 'hex')) :
1359
                                    null,
1360
                                blocktrailPublicKeys: blocktrailPublicKeys
1361
                            }
1362
                        ];
1363
                    });
1364
                }
1365
            );
1366
        })
1367
       .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); });
1368
1369
    return deferred.promise;
1370
};
1371
1372
APIClient.prototype._createNewWalletV3 = function(options) {
1373
    var self = this;
1374
1375
    var deferred = q.defer();
1376
1377
    // avoid modifying passed options
1378
    options = _.merge({}, options);
1379
1380
    determineDataStorageV2_3(options)
1381
        .then(function(options) {
1382
            options.passphrase = options.passphrase || options.password;
1383
            delete options.password;
1384
1385
            // avoid deprecated options
1386
            if (options.primaryPrivateKey) {
1387
                throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
1388
            }
1389
1390
            // seed should be provided or generated
1391
            options.primarySeed = options.primarySeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
1392
1393
            return options;
1394
        })
1395
        .then(function(options) {
1396
            return self.produceEncryptedDataV3(options, deferred.notify.bind(deferred));
1397
        })
1398
        .then(function(options) {
1399
            return doRemainingWalletDataV2_3(options, self.network, deferred.notify.bind(deferred));
1400
        })
1401
        .then(function(options) {
1402
            // create a checksum of our private key which we'll later use to verify we used the right password
1403
            var pubKeyHash = bitcoin.crypto.hash160(options.primaryPrivateKey.getPublicKeyBuffer());
1404
            var checksum = bitcoin.address.toBase58Check(pubKeyHash, self.network.pubKeyHash);
1405
            var keyIndex = options.keyIndex;
1406
1407
            // send the public keys and encrypted data to server
1408
            return self.storeNewWalletV3(
1409
                options.identifier,
1410
                [options.primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1411
                [options.backupPublicKey.toBase58(), "M"],
1412
                options.storeDataOnServer ? options.encryptedPrimarySeed : false,
1413
                options.storeDataOnServer ? options.encryptedSecret : false,
1414
                options.storeDataOnServer ? options.recoverySecret : false,
1415
                checksum,
1416
                keyIndex,
1417
                options.support_secret || null,
1418
                options.segwit || null
1419
            )
1420
                .then(
1421
                    // result, deferred, self(apiclient)
1422
                    function(result) {
1423
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1424
1425
                        var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1426
                            return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1427
                        });
1428
1429
                        var wallet = new Wallet(
1430
                            self,
1431
                            options.identifier,
1432
                            Wallet.WALLET_VERSION_V3,
1433
                            null,
1434
                            options.storeDataOnServer ? options.encryptedPrimarySeed : null,
1435
                            options.storeDataOnServer ? options.encryptedSecret : null,
1436
                            {keyIndex: options.primaryPublicKey},
1437
                            options.backupPublicKey,
1438
                            blocktrailPublicKeys,
1439
                            keyIndex,
1440
                            result.segwit || 0,
1441
                            self.testnet,
1442
                            checksum,
1443
                            result.upgrade_key_index,
1444
                            options.useCashAddress,
1445
                            options.bypassNewAddressCheck
1446
                        );
1447
1448
                        // pass along decrypted data to avoid extra work
1449
                        return wallet.unlock({
1450
                            walletVersion: Wallet.WALLET_VERSION_V3,
1451
                            passphrase: options.passphrase,
1452
                            primarySeed: options.primarySeed,
1453
                            secret: options.secret
1454
                        }).then(function() {
1455
                            deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1456
                            return [
1457
                                wallet,
1458
                                {
1459
                                    walletVersion: wallet.walletVersion,
1460
                                    encryptedPrimarySeed: options.encryptedPrimarySeed ? EncryptionMnemonic.encode(options.encryptedPrimarySeed) : null,
1461
                                    backupSeed: options.backupSeed ? bip39.entropyToMnemonic(options.backupSeed) : null,
1462
                                    recoveryEncryptedSecret: options.recoveryEncryptedSecret ?
1463
                                        EncryptionMnemonic.encode(options.recoveryEncryptedSecret) : null,
1464
                                    encryptedSecret: options.encryptedSecret ? EncryptionMnemonic.encode(options.encryptedSecret) : null,
1465
                                    blocktrailPublicKeys: blocktrailPublicKeys
1466
                                }
1467
                            ];
1468
                        });
1469
                    }
1470
                );
1471
        })
1472
        .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); });
1473
1474
    return deferred.promise;
1475
};
1476
1477
function verifyPublicBip32Key(bip32Key, network) {
1478
    var hk = bitcoin.HDNode.fromBase58(bip32Key[0], network);
1479
    if (typeof hk.keyPair.d !== "undefined") {
1480
        throw new Error('BIP32Key contained private key material - abort');
1481
    }
1482
1483
    if (bip32Key[1].slice(0, 1) !== "M") {
1484
        throw new Error("BIP32Key contained non-public path - abort");
1485
    }
1486
}
1487
1488
function verifyPublicOnly(walletData, network) {
1489
    verifyPublicBip32Key(walletData.primary_public_key, network);
1490
    verifyPublicBip32Key(walletData.backup_public_key, network);
1491
}
1492
1493
/**
1494
 * create wallet using the API
1495
 *
1496
 * @param identifier            string      the wallet identifier to create
1497
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1498
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1499
 * @param primaryMnemonic       string      mnemonic to store
1500
 * @param checksum              string      checksum to store
1501
 * @param keyIndex              int         keyIndex that was used to create wallet
1502
 * @param segwit                bool
1503
 * @returns {q.Promise}
1504
 */
1505
APIClient.prototype.storeNewWalletV1 = function(identifier, primaryPublicKey, backupPublicKey, primaryMnemonic,
1506
                                                checksum, keyIndex, segwit) {
1507
    var self = this;
1508
1509
    var postData = {
1510
        identifier: identifier,
1511
        wallet_version: Wallet.WALLET_VERSION_V1,
1512
        primary_public_key: primaryPublicKey,
1513
        backup_public_key: backupPublicKey,
1514
        primary_mnemonic: primaryMnemonic,
1515
        checksum: checksum,
1516
        key_index: keyIndex,
1517
        segwit: segwit
1518
    };
1519
1520
    verifyPublicOnly(postData, self.network);
1521
1522
    return self.client.post("/wallet", null, postData);
1523
};
1524
1525
/**
1526
 * create wallet using the API
1527
 *
1528
 * @param identifier            string      the wallet identifier to create
1529
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1530
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1531
 * @param encryptedPrimarySeed  string      openssl format
1532
 * @param encryptedSecret       string      openssl format
1533
 * @param recoverySecret        string      openssl format
1534
 * @param checksum              string      checksum to store
1535
 * @param keyIndex              int         keyIndex that was used to create wallet
1536
 * @param supportSecret         string
1537
 * @param segwit                bool
1538
 * @returns {q.Promise}
1539
 */
1540
APIClient.prototype.storeNewWalletV2 = function(identifier, primaryPublicKey, backupPublicKey, encryptedPrimarySeed, encryptedSecret,
1541
                                                recoverySecret, checksum, keyIndex, supportSecret, segwit) {
1542
    var self = this;
1543
1544
    var postData = {
1545
        identifier: identifier,
1546
        wallet_version: Wallet.WALLET_VERSION_V2,
1547
        primary_public_key: primaryPublicKey,
1548
        backup_public_key: backupPublicKey,
1549
        encrypted_primary_seed: encryptedPrimarySeed,
1550
        encrypted_secret: encryptedSecret,
1551
        recovery_secret: recoverySecret,
1552
        checksum: checksum,
1553
        key_index: keyIndex,
1554
        support_secret: supportSecret || null,
1555
        segwit: segwit
1556
    };
1557
1558
    verifyPublicOnly(postData, self.network);
1559
1560
    return self.client.post("/wallet", null, postData);
1561
};
1562
1563
/**
1564
 * create wallet using the API
1565
 *
1566
 * @param identifier            string      the wallet identifier to create
1567
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1568
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1569
 * @param encryptedPrimarySeed  Buffer      buffer of ciphertext
1570
 * @param encryptedSecret       Buffer      buffer of ciphertext
1571
 * @param recoverySecret        Buffer      buffer of recovery secret
1572
 * @param checksum              string      checksum to store
1573
 * @param keyIndex              int         keyIndex that was used to create wallet
1574
 * @param supportSecret         string
1575
 * @param segwit                bool
1576
 * @returns {q.Promise}
1577
 */
1578
APIClient.prototype.storeNewWalletV3 = function(identifier, primaryPublicKey, backupPublicKey, encryptedPrimarySeed, encryptedSecret,
1579
                                                recoverySecret, checksum, keyIndex, supportSecret, segwit) {
1580
    var self = this;
1581
1582
    var postData = {
1583
        identifier: identifier,
1584
        wallet_version: Wallet.WALLET_VERSION_V3,
1585
        primary_public_key: primaryPublicKey,
1586
        backup_public_key: backupPublicKey,
1587
        encrypted_primary_seed: encryptedPrimarySeed.toString('base64'),
1588
        encrypted_secret: encryptedSecret.toString('base64'),
1589
        recovery_secret: recoverySecret.toString('hex'),
1590
        checksum: checksum,
1591
        key_index: keyIndex,
1592
        support_secret: supportSecret || null,
1593
        segwit: segwit
1594
    };
1595
1596
    verifyPublicOnly(postData, self.network);
1597
1598
    return self.client.post("/wallet", null, postData);
1599
};
1600
1601
/**
1602
 * create wallet using the API
1603
 *
1604
 * @param identifier            string      the wallet identifier to create
1605
 * @param postData              object
1606
 * @param [cb]                  function    callback(err, result)
1607
 * @returns {q.Promise}
1608
 */
1609
APIClient.prototype.updateWallet = function(identifier, postData, cb) {
1610
    var self = this;
1611
1612
    return self.client.post("/wallet/" + identifier, null, postData, cb);
1613
};
1614
1615
/**
1616
 * upgrade wallet to use a new account number
1617
 *  the account number specifies which blocktrail cosigning key is used
1618
 *
1619
 * @param identifier            string      the wallet identifier
1620
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1621
 * @param keyIndex              int         keyIndex that was used to create wallet
1622
 * @param [cb]                  function    callback(err, result)
1623
 * @returns {q.Promise}
1624
 */
1625
APIClient.prototype.upgradeKeyIndex = function(identifier, keyIndex, primaryPublicKey, cb) {
1626
    var self = this;
1627
1628
    return self.client.post("/wallet/" + identifier + "/upgrade", null, {
1629
        key_index: keyIndex,
1630
        primary_public_key: primaryPublicKey
1631
    }, cb);
1632
};
1633
1634
/**
1635
 * get the balance for the wallet
1636
 *
1637
 * @param identifier            string      the wallet identifier
1638
 * @param [cb]                  function    callback(err, result)
1639
 * @returns {q.Promise}
1640
 */
1641
APIClient.prototype.getWalletBalance = function(identifier, cb) {
1642
    var self = this;
1643
1644
    return self.client.get("/wallet/" + identifier + "/balance", null, true, cb);
1645
};
1646
1647
/**
1648
 * do HD wallet discovery for the wallet
1649
 *
1650
 * @param identifier            string      the wallet identifier
1651
 * @param [cb]                  function    callback(err, result)
1652
 * @returns {q.Promise}
1653
 */
1654
APIClient.prototype.doWalletDiscovery = function(identifier, gap, cb) {
1655
    var self = this;
1656
1657
    return self.client.get("/wallet/" + identifier + "/discovery", {gap: gap}, true, cb);
1658
};
1659
1660
1661
/**
1662
 * get a new derivation number for specified parent path
1663
 *  eg; m/44'/1'/0/0 results in m/44'/1'/0/0/0 and next time in m/44'/1'/0/0/1 and next time in m/44'/1'/0/0/2
1664
 *
1665
 * @param identifier            string      the wallet identifier
1666
 * @param path                  string      the parent path for which to get a new derivation,
1667
 *                                           can be suffixed with /* to make it clear on which level the derivations hould be
1668
 * @param [cb]                  function    callback(err, result)
1669
 * @returns {q.Promise}
1670
 */
1671
APIClient.prototype.getNewDerivation = function(identifier, path, cb) {
1672
    var self = this;
1673
1674
    return self.client.post("/wallet/" + identifier + "/path", null, {path: path}, cb);
1675
};
1676
1677
1678
/**
1679
 * delete the wallet
1680
 *  the checksum address and a signature to verify you ownership of the key of that checksum address
1681
 *  is required to be able to delete a wallet
1682
 *
1683
 * @param identifier            string      the wallet identifier
1684
 * @param checksumAddress       string      the address for your master private key (and the checksum used when creating the wallet)
1685
 * @param checksumSignature     string      a signature of the checksum address as message signed by the private key matching that address
1686
 * @param [force]               bool        ignore warnings (such as a non-zero balance)
1687
 * @param [cb]                  function    callback(err, result)
1688
 * @returns {q.Promise}
1689
 */
1690
APIClient.prototype.deleteWallet = function(identifier, checksumAddress, checksumSignature, force, cb) {
1691
    var self = this;
1692
1693
    if (typeof force === "function") {
1694
        cb = force;
1695
        force = false;
1696
    }
1697
1698
    return self.client.delete("/wallet/" + identifier, {force: force}, {
1699
        checksum: checksumAddress,
1700
        signature: checksumSignature
1701
    }, cb);
1702
};
1703
1704
/**
1705
 * use the API to get the best inputs to use based on the outputs
1706
 *
1707
 * the return array has the following format:
1708
 * [
1709
 *  "utxos" => [
1710
 *      [
1711
 *          "hash" => "<txHash>",
1712
 *          "idx" => "<index of the output of that <txHash>",
1713
 *          "scriptpubkey_hex" => "<scriptPubKey-hex>",
1714
 *          "value" => 32746327,
1715
 *          "address" => "1address",
1716
 *          "path" => "m/44'/1'/0'/0/13",
1717
 *          "redeem_script" => "<redeemScript-hex>",
1718
 *      ],
1719
 *  ],
1720
 *  "fee"   => 10000,
1721
 *  "change"=> 1010109201,
1722
 * ]
1723
 *
1724
 * @param identifier        string      the wallet identifier
1725
 * @param pay               array       {'address': (int)value}     coins to send
1726
 * @param lockUTXO          bool        lock UTXOs for a few seconds to allow for transaction to be created
1727
 * @param allowZeroConf     bool        allow zero confirmation unspent outputs to be used in coin selection
1728
 * @param feeStrategy       string      defaults to
1729
 * @param options
1730
 * @param [cb]              function    callback(err, utxos, fee, change)
1731
 * @returns {q.Promise}
1732
 */
1733
APIClient.prototype.coinSelection = function(identifier, pay, lockUTXO, allowZeroConf, feeStrategy, options, cb) {
1734
    var self = this;
1735
1736
    if (typeof feeStrategy === "function") {
1737
        cb = feeStrategy;
1738
        feeStrategy = null;
1739
        options = {};
1740
    } else if (typeof options === "function") {
1741
        cb = options;
1742
        options = {};
1743
    }
1744
1745
    feeStrategy = feeStrategy || Wallet.FEE_STRATEGY_OPTIMAL;
1746
    options = options || {};
1747
1748
    var deferred = q.defer();
1749
    deferred.promise.spreadNodeify(cb);
1750
1751
    var params = {
1752
        lock: lockUTXO,
1753
        zeroconf: allowZeroConf ? 1 : 0,
1754
        zeroconfself: (typeof options.allowZeroConfSelf !== "undefined" ? options.allowZeroConfSelf : true) ? 1 : 0,
1755
        fee_strategy: feeStrategy
1756
    };
1757
1758
    if (options.forcefee) {
1759
        params['forcefee'] = options.forcefee;
1760
    }
1761
1762
    deferred.resolve(
1763
        self.client.post("/wallet/" + identifier + "/coin-selection", params, pay).then(
1764
            function(result) {
1765
                return [result.utxos, result.fee, result.change, result];
1766
            },
1767
            function(err) {
1768
                if (err.message.match(/too low to pay the fee/)) {
1769
                    throw blocktrail.WalletFeeError(err);
1770
                }
1771
1772
                throw err;
1773
            }
1774
        )
1775
    );
1776
1777
    return deferred.promise;
1778
};
1779
1780
/**
1781
 * @param [cb]              function    callback(err, utxos, fee, change)
1782
 * @returns {q.Promise}
1783
 */
1784
APIClient.prototype.feePerKB = function(cb) {
1785
    var self = this;
1786
1787
    var deferred = q.defer();
1788
    deferred.promise.spreadNodeify(cb);
1789
1790
    deferred.resolve(self.client.get("/fee-per-kb"));
1791
1792
    return deferred.promise;
1793
};
1794
1795
/**
1796
 * send the transaction using the API
1797
 *
1798
 * @param identifier        string      the wallet identifier
1799
 * @param txHex             string      partially signed transaction as hex string
1800
 * @param paths             array       list of paths used in inputs which should be cosigned by the API
1801
 * @param checkFee          bool        when TRUE the API will verify if the fee is 100% correct and otherwise throw an exception
1802
 * @param [twoFactorToken]  string      2FA token
1803
 * @param [prioboost]       bool
1804
 * @param [cb]              function    callback(err, txHash)
1805
 * @returns {q.Promise}
1806
 */
1807
APIClient.prototype.sendTransaction = function(identifier, txHex, paths, checkFee, twoFactorToken, prioboost, cb) {
1808
    var self = this;
1809
1810
    if (typeof twoFactorToken === "function") {
1811
        cb = twoFactorToken;
1812
        twoFactorToken = null;
1813
        prioboost = false;
1814
    } else if (typeof prioboost === "function") {
1815
        cb = prioboost;
1816
        prioboost = false;
1817
    }
1818
1819
    var data = {
1820
        paths: paths,
1821
        two_factor_token: twoFactorToken
1822
    };
1823
    if (typeof txHex === "string") {
1824
        data.raw_transaction = txHex;
1825
    } else if (typeof txHex === "object") {
1826
        Object.keys(txHex).map(function(key) {
1827
            data[key] = txHex[key];
1828
        });
1829
    }
1830
1831
    return self.client.post(
1832
        "/wallet/" + identifier + "/send",
1833
        {
1834
            check_fee: checkFee ? 1 : 0,
1835
            prioboost: prioboost ? 1 : 0
1836
        },
1837
        data,
1838
        cb
1839
    );
1840
};
1841
1842
/**
1843
 * setup a webhook for this wallet
1844
 *
1845
 * @param identifier        string      the wallet identifier
1846
 * @param webhookIdentifier string      identifier for the webhook
1847
 * @param url               string      URL to receive webhook events
1848
 * @param [cb]              function    callback(err, webhook)
1849
 * @returns {q.Promise}
1850
 */
1851
APIClient.prototype.setupWalletWebhook = function(identifier, webhookIdentifier, url, cb) {
1852
    var self = this;
1853
1854
    return self.client.post("/wallet/" + identifier + "/webhook", null, {url: url, identifier: webhookIdentifier}, cb);
1855
};
1856
1857
/**
1858
 * delete a webhook that was created for this wallet
1859
 *
1860
 * @param identifier        string      the wallet identifier
1861
 * @param webhookIdentifier string      identifier for the webhook
1862
 * @param [cb]              function    callback(err, success)
1863
 * @returns {q.Promise}
1864
 */
1865
APIClient.prototype.deleteWalletWebhook = function(identifier, webhookIdentifier, cb) {
1866
    var self = this;
1867
1868
    return self.client.delete("/wallet/" + identifier + "/webhook/" + webhookIdentifier, null, null, cb);
1869
};
1870
1871
/**
1872
 * get all transactions for an wallet (paginated)
1873
 *
1874
 * @param identifier    string      wallet identifier
1875
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1876
 * @param [cb]          function    callback function to call when request is complete
1877
 * @return q.Promise
1878
 */
1879
APIClient.prototype.walletTransactions = function(identifier, params, cb) {
1880
    var self = this;
1881
1882
    if (typeof params === "function") {
1883
        cb = params;
1884
        params = null;
1885
    }
1886
1887
    return self.client.get("/wallet/" + identifier + "/transactions", params, true, cb);
1888
};
1889
1890
/**
1891
 * get all addresses for an wallet (paginated)
1892
 *
1893
 * @param identifier    string      wallet identifier
1894
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1895
 * @param [cb]          function    callback function to call when request is complete
1896
 * @return q.Promise
1897
 */
1898
APIClient.prototype.walletAddresses = function(identifier, params, cb) {
1899
    var self = this;
1900
1901
    if (typeof params === "function") {
1902
        cb = params;
1903
        params = null;
1904
    }
1905
1906
    return self.client.get("/wallet/" + identifier + "/addresses", params, true, cb);
1907
};
1908
1909
/**
1910
 * @param identifier    string      wallet identifier
1911
 * @param address       string      the address to label
1912
 * @param label         string      the label
1913
 * @param [cb]          function    callback(err, res)
1914
 * @return q.Promise
1915
 */
1916
APIClient.prototype.labelWalletAddress = function(identifier, address, label, cb) {
1917
    var self = this;
1918
1919
    return self.client.post("/wallet/" + identifier + "/address/" + address + "/label", null, {label: label}, cb);
1920
};
1921
1922
APIClient.prototype.walletMaxSpendable = function(identifier, allowZeroConf, feeStrategy, options, cb) {
1923
    var self = this;
1924
1925
    if (typeof feeStrategy === "function") {
1926
        cb = feeStrategy;
1927
        feeStrategy = null;
1928
    } else if (typeof options === "function") {
1929
        cb = options;
1930
        options = {};
1931
    }
1932
1933
    feeStrategy = feeStrategy || Wallet.FEE_STRATEGY_OPTIMAL;
1934
    options = options || {};
1935
1936
    var params = {
1937
        outputs: options.outputs ? options.outputs : 1,
1938
        zeroconf: allowZeroConf ? 1 : 0,
1939
        zeroconfself: (typeof options.allowZeroConfSelf !== "undefined" ? options.allowZeroConfSelf : true) ? 1 : 0,
1940
        fee_strategy: feeStrategy
1941
    };
1942
1943
    if (options.forcefee) {
1944
        params['forcefee'] = options.forcefee;
1945
    }
1946
1947
    return self.client.get("/wallet/" + identifier + "/max-spendable", params, true, cb);
1948
};
1949
1950
/**
1951
 * get all UTXOs for an wallet (paginated)
1952
 *
1953
 * @param identifier    string      wallet identifier
1954
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1955
 * @param [cb]          function    callback function to call when request is complete
1956
 * @return q.Promise
1957
 */
1958
APIClient.prototype.walletUTXOs = function(identifier, params, cb) {
1959
    var self = this;
1960
1961
    if (typeof params === "function") {
1962
        cb = params;
1963
        params = null;
1964
    }
1965
1966
    return self.client.get("/wallet/" + identifier + "/utxos", params, true, cb);
1967
};
1968
1969
/**
1970
 * get a paginated list of all wallets associated with the api user
1971
 *
1972
 * @param [params]      object      pagination: {page: 1, limit: 20}
1973
 * @param [cb]          function    callback function to call when request is complete
1974
 * @return q.Promise
1975
 */
1976
APIClient.prototype.allWallets = function(params, cb) {
1977
    var self = this;
1978
1979
    if (typeof params === "function") {
1980
        cb = params;
1981
        params = null;
1982
    }
1983
1984
    return self.client.get("/wallets", params, true, cb);
1985
};
1986
1987
/**
1988
 * verify a message signed bitcoin-core style
1989
 *
1990
 * @param message        string
1991
 * @param address        string
1992
 * @param signature      string
1993
 * @param [cb]          function    callback function to call when request is complete
1994
 * @return q.Promise
1995
 */
1996
APIClient.prototype.verifyMessage = function(message, address, signature, cb) {
1997
    var self = this;
1998
1999
    // we could also use the API instead of the using bitcoinjs-lib to verify
2000
    // return self.client.post("/verify_message", null, {message: message, address: address, signature: signature}, cb);
2001
2002
    var deferred = q.defer();
2003
    deferred.promise.nodeify(cb);
2004
    try {
2005
        var result = bitcoinMessage.verify(address, self.network.messagePrefix, message, new Buffer(signature, 'base64'));
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
2006
        deferred.resolve(result);
2007
    } catch (e) {
2008
        deferred.reject(e);
2009
    }
2010
2011
    return deferred.promise;
2012
};
2013
2014
/**
2015
 * max is 0.001
2016
 * testnet only
2017
 *
2018
 * @param address
2019
 * @param amount
2020
 * @param cb
2021
 */
2022
APIClient.prototype.faucetWithdrawl = function(address, amount, cb) {
2023
    var self = this;
2024
2025
    return self.client.post("/faucet/withdrawl", null, {address: address, amount: amount}, cb);
2026
};
2027
2028
/**
2029
 * send a raw transaction
2030
 *
2031
 * @param rawTransaction    string      raw transaction as HEX
2032
 * @param [cb]              function    callback function to call when request is complete
2033
 * @return q.Promise
2034
 */
2035
APIClient.prototype.sendRawTransaction = function(rawTransaction, cb) {
2036
    var self = this;
2037
2038
    return self.client.post("/send-raw-tx", null, rawTransaction, cb);
2039
};
2040
2041
/**
2042
 * get the current price index
2043
 *
2044
 * @param [cb]          function    callback({'USD': 287.30})
2045
 * @return q.Promise
2046
 */
2047
APIClient.prototype.price = function(cb) {
2048
    var self = this;
2049
2050
    return self.client.get("/price", null, false, cb);
2051
};
2052
2053
module.exports = APIClient;
2054